﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using CoreEx.Common.Extensions;
using CoreEx.DynamicInvoker;
using LinFu.AOP.Interfaces;
using LinFu.Proxy.Interfaces;

namespace CoreEx.Database.Persistence
{
    public class ListInvokeWrapper : IInvokeWrapper
    {
        private object _targetList;
        private IChangeTracker _changeTracker;
        private IDictionary<object,object> _proxyItems = new Dictionary<object, object>();
        private Type _elementType;
        private IEnumerator _enumerator;

        public ListInvokeWrapper(object targetList, IChangeTracker changeTracker)        
        {
            _enumerator = ((IEnumerable) targetList).GetEnumerator();
            _elementType = targetList.GetType().GetGenericElementType();
            _targetList = targetList;
            _changeTracker = changeTracker;
        }


        public void BeforeInvoke(IInvocationInfo info)
        {
            //throw new NotImplementedException();
        }

        public void AfterInvoke(IInvocationInfo info, object returnValue)
        {
            //throw new NotImplementedException();
        }

        public object DoInvoke(IInvocationInfo info)
        {            
            if (info.TargetMethod.Name == "GetEnumerator")
            {
                var enumeratorProxy = GetEnumeratorProxy();
                return enumeratorProxy;
            }
            
            if (info.TargetMethod.Name == "get_Item")
            {
                var actualItem = info.TargetMethod.DynamicInvoke(_targetList, info.Arguments);
                return GetProxyInstance(actualItem);
            }
            return info.TargetMethod.DynamicInvoke(_targetList, info.Arguments);
        }


        private IEnumerator GetEnumeratorProxy()
        {
            var enumerator = ((IEnumerable)_targetList).GetEnumerator();
            var enumeratorProxy = _changeTracker.ProxyFactory.CreateProxy(typeof(IEnumerator<>).MakeGenericType(_elementType),
                                                                          new EnumeratorInvokeWrapper(enumerator,
                                                                                                      _proxyItems,
                                                                                                      _changeTracker, _elementType));
            return (IEnumerator)enumeratorProxy;    
        }


        public object GetProxyInstance(object target)
        {
            if (target == null)
                return null;

            if (!_proxyItems.ContainsKey(target))
            {
                var proxy = CreateProxy(target);
                _proxyItems.Add(target, proxy);
            }
            return _proxyItems[target];
        }


        private object CreateProxy(object target)
        {
            return _changeTracker.ProxyFactory.CreateProxy(_elementType, 
                new ObjectInvokeWrapper(target, _changeTracker), typeof(INotifyPropertyChanged));                                                                           
        }

    }
}
